package com.amumu.drama.common.commpont.notify;


import com.alibaba.fastjson.TypeReference;
import com.amumu.drama.common.config.NotifyConfig;
import com.amumu.drama.common.utils.http.HttpClientUtils;
import com.amumu.drama.common.utils.json.JsonObjectUtils;
import com.conversantmedia.util.concurrent.DisruptorBlockingQueue;
import com.conversantmedia.util.concurrent.SpinPolicy;
import com.google.common.io.BaseEncoding;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author sunzhuang
 * @date 2024/10/25 10:22
 * @slogan 天下风云出我辈, 一入代码岁月催
 * @desc todo
 */

public class Notifier extends Thread {

    private static final Logger LOG = LoggerFactory.getLogger(Notifier.class);

    private static final String DEFAULT_ROBOT_GROUP = "default";
    private static final AtomicBoolean running = new AtomicBoolean(false);
    private static final DisruptorBlockingQueue<String> QUEUE = new DisruptorBlockingQueue<>(4096, SpinPolicy.BLOCKING);
    private static final MultiValueMap<String, NotifyConfig.Robot> ROBOT_GROUP_MAP = new LinkedMultiValueMap<>();

    private static final CloseableHttpClient HTTP_CLIENT = HttpClientUtils.createHttpClient();

    public Notifier() {
        super("etnaLogNotifier");
        setDaemon(true);
    }

    public static void initRobotGroupMap(NotifyConfig nc) {
        List<NotifyConfig.Robot> robots = nc.getRobots();
        if (robots == null || robots.size() == 0) {
            return;
        }

        ROBOT_GROUP_MAP.clear();
        for (NotifyConfig.Robot r : robots) {
            if (StringUtils.isBlank(r.getUrl())) {
                LOG.warn("EtnaLogNotifier: invalid notify robot url");
                continue;
            }

            String group = r.getGroup();
            if (StringUtils.isBlank(group)) {
                ROBOT_GROUP_MAP.add(DEFAULT_ROBOT_GROUP, r);
            } else {
                ROBOT_GROUP_MAP.add(group, r);
            }
        }
    }

    public static void sendNotify(String msg) {
        Set<String> groupSet = ROBOT_GROUP_MAP.keySet();
        if (groupSet.size() == 0) {
            LOG.warn("EtnaLogNotifier: no notify robot group found");
            return;
        }

        for (String g : groupSet) {
            List<NotifyConfig.Robot> robots = ROBOT_GROUP_MAP.getOrDefault(g, null);
            if (robots == null || robots.size() == 0) {
                LOG.warn("EtnaLogNotifier: no notify robot found（group={}）", g);
                continue;
            }
            int idx = RandomUtils.nextInt(0, robots.size());
            NotifyConfig.Robot robot = robots.get(idx);
            if (robot.getUrl().contains("feishu")) {
                sendFeishuNotify(msg, robot);
            } else {
                sendDingDingNotify(msg, robot);
            }
        }
    }

    private static void sendFeishuNotify(String msg, NotifyConfig.Robot robot) {
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(3000)
                    .setSocketTimeout(3000)
                    .setConnectTimeout(1000)
                    .build();

            String url = robot.getUrl();
            String secretKey = robot.getSecretKey();
            Map<String, Object> postMap = JsonObjectUtils.parseJson(msg, new TypeReference<Map<String, Object>>() {
            });
            if (StringUtils.isNotBlank(secretKey)) {
                try {
                    long timestamp = System.currentTimeMillis() / 1000L;
                    Mac mac = Mac.getInstance("HmacSHA256");
                    mac.init(new SecretKeySpec((timestamp + "\n" + secretKey).getBytes(), "HmacSHA256"));
                    byte[] signData = mac.doFinal();
                    String sign = BaseEncoding.base64().encode(signData);

                    postMap.put("timestamp", "" + timestamp);
                    postMap.put("sign", sign);
                } catch (Exception ignore) {
                }
            }

            HttpPost request = new HttpPost(url);
            request.setConfig(requestConfig);
            request.setEntity(new StringEntity(JsonObjectUtils.createJson(postMap), ContentType.APPLICATION_JSON));
            try (CloseableHttpResponse response = HTTP_CLIENT.execute(request)) {
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    String respContent = EntityUtils.toString(response.getEntity());
                    LOG.info("EtnaLogNotifier: send notify. response={}", respContent);
                    return;
                }
                String respContent = EntityUtils.toString(response.getEntity());
                LOG.warn("EtnaLogNotifier: send notify fail. response={}", respContent);
            }
        } catch (ParseException | IOException e) {
            LOG.warn("EtnaLogNotifier: sendNotify error", e);
        }
    }

    private static void sendDingDingNotify(String msg, NotifyConfig.Robot robot) {
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(3000)
                    .setSocketTimeout(3000)
                    .setConnectTimeout(1000)
                    .build();

            String url = robot.getUrl();
            String secretKey = robot.getSecretKey();

            Map<String, Object> postMap = new LinkedHashMap<>();
            postMap.put("msgtype", "text");
            Map<String, String> contentMap = new LinkedHashMap<>();
            postMap.put("text", contentMap);
            contentMap.put("content", msg);

            if (StringUtils.isNotBlank(secretKey)) {
                try {
                    long timestamp = System.currentTimeMillis();
                    Mac mac = Mac.getInstance("HmacSHA256");
                    mac.init(new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
                    byte[] signData = mac.doFinal((timestamp + "\n" + secretKey).getBytes(StandardCharsets.UTF_8));
                    String sign = BaseEncoding.base64().encode(signData);

                    url += "&timestamp=" + timestamp + "&sign=" + sign;
                } catch (Exception ignore) {
                }
            }

            HttpPost request = new HttpPost(url);
            request.setConfig(requestConfig);
            request.setEntity(new StringEntity(JsonObjectUtils.createJson(postMap), ContentType.APPLICATION_JSON));
            try (CloseableHttpResponse response = HTTP_CLIENT.execute(request)) {
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    String respContent = EntityUtils.toString(response.getEntity());
                    LOG.info("EtnaLogNotifier: send notify. response={}", respContent);
                    return;
                }
                String respContent = EntityUtils.toString(response.getEntity());
                LOG.warn("EtnaLogNotifier: send notify fail. response={}", respContent);
            }
        } catch (ParseException | IOException e) {
            LOG.warn("EtnaLogNotifier: sendNotify error", e);
        }
    }

    @Override
    public synchronized void start() {
        if (running.get()) {
            LOG.info("EtnaLogNotifier: had already started ...");
            return;
        }
        LOG.info("EtnaLogNotifier: start ...");
        super.start();
        running.set(true);
    }

    @Override
    public void run() {
        while (running.get()) {
            try {
                String msg = QUEUE.take();
                if (NotifyConfigLoader.NOTIFY_CONFIG.getEnable() == 0) {
                    return;
                }
                sendNotify(msg);
            } catch (InterruptedException e) {

            }
        }
        LOG.info("EtnaLogNotifier: stop...");
    }

    @Override
    public void interrupt() {
        running.set(false);
        super.interrupt();
    }

    public void shutdown() {
        running.set(false);
    }

    public void addMessage(String msg) {
        QUEUE.offer(msg);
    }

    public static class Holder {
        public static final Notifier INSTANT = new Notifier();
    }
}
